接下來就是跟資料庫連結了,會著重說明 ASP.NET Core 跟 EF Core,如果有需要改畫面才會說到 Blazor,這邊會花比較多篇幅著墨。
首先要把 Blog 跟 Post 分開,我們先在NavMenu.razor
加入指向 Blog 的連結,接著將Blog.razor
改寫,因為BlogId
必須有值且大於0才是一筆部落格資料,所以我們在BlogId
等於0時顯示一個表單讓使用者輸入部落格名稱,另外在BlogModel
的BlogName
加上Required
attribute,畢竟一個部落格要有名字。
不等於0的話則顯示部落格名稱及底下的日誌,且部落格名稱改用<h3>
元素。
12行有個參數OnValidSubmit="createBlog"
,代表的是如果表單驗證通過,則執行指定的 function createBlog
,筆者先寫好一個放在那裡,等說完取資料的部分會再說明。
(註:如果希望觸發OnValidSubmit
,必須有個type="submit"
的按鈕,否則不管怎麼按都沒效果)
有人問那如果BlogId
小於0呢?因為這個欄位是資料庫由1開始遞增產生,通常不會有這問題,除非有人竄改資料庫,真的擔心的話可以在資料庫加入不可小於0的機制。
畫面有了,接著來取資料,我們在根目錄建立一個資料夾 Repository,建立一個介面 interface IBlogRepository
,Repository 裡面再建立一個資料夾 Implement,建立一個類別 class BlogRepository
,資料夾結構因人而異,筆者是因為看到同事這樣用,覺得可以快速找到介面跟實作很方便。
介面做的事情很簡單,就是規範方法;實作的 13 到 18 行是依賴注入AppDbContext
;19 到 27行是取得第一筆 Blog,21行的 Include() 就是昨天說的不用自己 join table的方法,全名為Eager loading
,只要建表的時候有建好關聯,就可以節省時間,其他還有Explicit loading
及Lazy loading
,都是 Entity Framework Core 提供的方便作法,如果是一對一的關聯在 SQL 語法會被翻譯成 Inner Join
,一對多則是 Left Join
。
正常來說 Blog 會跟登入者 (User) 綁定,可能有張 Table 記錄BlogId
跟UserId
,但目前還沒實作登入機制,所以就取第一筆資料,如果沒有第一筆代表還沒建立,回傳空的 Blog;28 到 43行則是建立 Blog 的方法,30 到 31 行先檢查 Blog 是否存在,不存在則建立 Blog,而不論是否存在,都回傳型別為ResultViewModel
的物件,裡面只有兩個 Property:IsSuccess
、Message
,前端收到後是否要根據IsSuccess
做事,就是前端的事了。
接著去Startup.cs
註冊剛才寫的 Repository,有些人可能會問,上面的GuidService
跟之前建立的PostService
為什麼叫 Service 這邊卻叫 Repository,這是筆者的習慣,筆者此前開發的 Blazor 專案是多層式架構,分別為後端 API、中介 Model、前端 Blazor,後端處理資料的檔案都以 Repository做為後綴詞,前端取得資料的檔案則以 Service 做為後綴詞,端看不同公司的開發模式。後來筆者將 Service 抽成另一個專案,再後來又將 DbContext 抽成一個專案,都是為了方便日後有其他專案要參考 Model 或是只是要調動 Service 的話,不需要參考整個 Blazor 或是 API 專案。
最後就是BlogBase.razor.cs
了,16 到 17 行注入IBlogRepository
、IJSRuntime
,23 到 27 行改成非同步方法,因為取資料、存資料都用非同步,所以這邊也要跟進;28到 31 行就是呼叫剛才寫的GetBlog()
方法取得 Blog;32 到 43 行是這篇一開始說到的createBlog()
方法,這裡如果成功的話就取得 Blog 資料,失敗則用前面說過的JsInteropClasses
顯示後端來的Message
。
接著讓我們輸入部落格名稱,輸入送出後,可以看到畫面不同了,去看資料庫,Blog 也能看到一筆資料。
前面說完 Blog,今天來說 Post 的部分,先建立IPostRepository
跟PostRepository
,裡面只有CreatePost()
跟DeletePost()
兩個方法,那怎麼沒有取得單篇的GetPost()
或是多篇的GetPosts()
方法呢?因為 Blog 的GetBlog()
已經帶入 Posts 所以不需要GetPosts()
,如果之後有需要看到個別日誌的話,再建立GetPost()
。
(註:筆者參與的系統不多,也不知道這樣的系統規劃是否常見,只是因為一開始沒有完整規劃才會這樣做。)
CreatePost()
如果在資料庫找不到 Post 就新增一筆,找得到就修改,既然要修改就要有修改時間,所以我們在PostModel.cs
加上UpdateDateTime
欄位,用指令Add-Migration
新增一個 Migration 後下指令Update-Database
更新資料庫,然後去Startup.cs
註冊這個新建的 Repository。
(註:前面說過的內容筆者就不附圖了,避免占版面。)
再來去PostBase.razor.cs
注入PostRepository
,改寫一下deletePost()
,新增一個按下Submit
按鈕會觸發的事件createPost()
,PostBase.razor
加上前面加入的更新時間UpdateDateTime
、OnValidSubmit="createPost"
,Submit
按鈕的type
改成submit
,讓表格真的有送出功能。
接下來就是重頭戲了,我們回到BlogBase.razor.cs
,把postId
刪除,因為用不到了,add()
則改成傳BlogId
跟CreateDateTime
到新的PostModel
,讓這筆日誌知道是跟著哪個 Blog。
產生CreateDateTime
跟UpdateDateTime
有兩種選擇:在前端或後端決定,端看系統如何規劃,因為這邊在 add 一筆 Post 的當下要避免看到0001-01-01 12:00:00
這樣的尷尬時間,所以我們在前端給DateTime.Now
,至於後端要不要覆蓋掉前端來的時間,就是規劃問題了。
接著把PostModel.Content
的[MinLength]
改成50,因為不想打太多字XD。
打完內容後點擊 Submit 按鈕,我們的第一篇日誌就建立成功了!
實際去看資料庫,可以看到有資料,跟 BlogId 的關聯也正確。
不過時間好像怪怪的,明明是晚上卻顯示早上時間,我們把Post.razor
的@bind:format
的小時格式改成大寫H,畫面的時間就正常了。
(註:因為這邊的操作是以前學的,也忘記在哪裡看的,就附上學習來源)
Ref: Blazor tutorial for beginners
Ref: Loading Related Data